﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using CLUBsInterpreter.AST;
using CLUBsInterpreter.ObjectTypes;

namespace CLUBsInterpreter.AST
{

	/**
	 * <summary>
	 * Describes a CLUBs definition in the AST.
	 * Inherits from BlockNode.
	 * </summary>
	 **/
	abstract class DefinitionNode : BlockNode
	{
		/**
		 * <summary>
		 * Private field containing a reference to the CLUBsObject defined.
		 * </summary>
		 **/
		private CLUBsObject m_objRef;

		/**
		 * <summary>
		 * The Name of the block.
		 * (I believe this should/will always be the same as this.BlockName?)
		 * </summary>
		 **/
		public string Name;
		/**
		 * <summary>
		 * The type of this definition.
		 * </summary>
		 **/
		public IdentifierType DefinitionType;
		/**
		 * <summary>
		 * Property for accessing the CLUBsObject reference.
		 * This is used for an extra layer of type checking, as we know the definition type of this definition,
		 * but use a generic CLUBsObject reference of a type corresponding to the definition type.
		 * </summary>
		 **/
		public CLUBsObject ObjectReference
		{
			set
			{
				// Check that the type of the new object is the same as this one
				if (value.GetType() == CLUBsObject.GetObjectType(DefinitionType))
					m_objRef = value;
				// If not, throw an exception
				else
					throw new InvalidOperationException("Attempt to assign wrong type of CLUBsObject to a DefinitionNode.");
			}
			get
			{
				// Just return the object reference
				return m_objRef;
			}
		}

		/**
		 * <summary>
		 * Static function generating a definition node for a specific identifier type.
		 * </summary>
		 * <param name="type"> The identifier type </param>
		 * <returns> A new DefinitionNode instance corresponding to the identifier type.
		 **/
		public static DefinitionNode GenerateNode(IdentifierType type)
		{
			// Return new object depending on the given type
			switch (type)
			{
				case IdentifierType.Action:
					return new ActionDefNode();
				case IdentifierType.Board:
					return new BoardDefNode();
				case IdentifierType.Cell:
					return new CellDefNode();
				case IdentifierType.Piece:
					return new PieceDefNode();
				case IdentifierType.Player:
					return new PlayerDefNode();
				case IdentifierType.Round:
					return new RoundDefNode();
				case IdentifierType.Turn:
					return new TurnDefNode();
				default:
					return null;
			}
		}
	}

	/**
	 * <summary>
	 * Describes an action definition node.
	 * </summary>
	 **/
	class ActionDefNode : DefinitionNode
	{
		/**
		 * <summary>
		 * The CLUBsAction object associated with the definition.
		 * </summary>
		 **/
		public CLUBsAction Action;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitDefinitionNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitDefinitionNode(this);
			}
		}
		
		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the action node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "ActionDefNode ('action "+Name+"')");
			// Call the block PrintTree
			base.PrintTree(outStream, depth + 1);
		}
	}

	class BoardDefNode : DefinitionNode
	{
		/**
		 * <summary>
		 * The CLUBsBoard object associated with the definition.
		 * </summary>
		 **/
		public CLUBsBoard Board;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitDefinitionNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitDefinitionNode(this);
			}
		}
		
		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the board node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "BoardDefNode ('board " + Name + "')");
			// Print block
			base.PrintTree(outStream, depth + 1);
		}
	}

	class CellDefNode : DefinitionNode
	{
		/**
		 * <summary>
		 * The CLUBsCell object associated with the definition.
		 * </summary>
		 **/
		public CLUBsCell Cell;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitDefinitionNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitDefinitionNode(this);
			}
		}
		
		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the cell node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "CellDefNode ('cell " + Name + "')");
			// Print block
			base.PrintTree(outStream, depth + 1);
		}
	}

	class PieceDefNode : DefinitionNode
	{
		/**
		 * <summary>
		 * The CLUBsPiece object associated with the definition.
		 * </summary>
		 **/
		public CLUBsPiece Piece;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitDefinitionNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitDefinitionNode(this);
			}
		}
		
		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the piece node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "PieceDefNode ('piece " + Name + "')");
			// Print block
			base.PrintTree(outStream, depth + 1);
		}
	}

	class PlayerDefNode : DefinitionNode
	{
		/**
		 * <summary>
		 * The CLUBsPlayer object associated with the definition.
		 * </summary>
		 **/
		public CLUBsPlayer Player;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitDefinitionNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitDefinitionNode(this);
			}
		}
		
		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the player node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "PlayerDefNode ('player " + Name + "')");
			// Print block
			base.PrintTree(outStream, depth + 1);
		}
	}

	class RoundDefNode : DefinitionNode
	{
		/**
		 * <summary>
		 * The CLUBsRound object associated with the definition.
		 * </summary>
		 **/
		public CLUBsRound Round;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitDefinitionNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitDefinitionNode(this);
			}
		}
		
		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the round node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "RoundDefNode ('round " + Name + "')");
			// Print block
			base.PrintTree(outStream, depth + 1);
		}
	}

	class TurnDefNode : DefinitionNode
	{
		/**
		 * <summary>
		 * The CLUBsTurn object associated with the definition.
		 * </summary>
		 **/
		public CLUBsTurn Turn;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitDefinitionNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitDefinitionNode(this);
			}
		}
		
		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the turn node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "TurnDefNode ('turn " + Name + "')");
			// Print block
			base.PrintTree(outStream, depth + 1);
		}
	}
}
